#include "genomesegment.h"

#include "gene.h"
#include "../../dynamicvalue.h"
//#include "../../visbasecollection.h"

GenomeSegment::GenomeSegment(QGraphicsItem *parent) :
    VisBaseCollection(parent)//QGraphicsWidget(parent)
{
    //initial values- will be changed
    height = parent->boundingRect().height();
    width = parent->boundingRect().width();

    dynX->setValue( 0.0 );
    dynY->setValue( 0.0 ); //init 0, no need to store anything yet
    dynWidth->setValue( width );
    dynHeight->setValue( height );

    setAcceptsHoverEvents(true);

    flip = false;
    updated = false;
    ntInGenomeSegment = 0;
    hovering = false;
    setCacheMode(QGraphicsItem::ItemCoordinateCache);//this call is magic- render to pixmap makes it faster

    minNT = 0;
    maxNT = ntInGenomeSegment;

//    qDebug() << "test map: val = 43, aMin = 0, aMax = 100, bMin = 0, bMax = 1000 " << map( 43, 0 , 100, 0, 1000 ) << endl;
//    qDebug() << "test map: val = 430, aMin = 0, aMax = 1000, bMin = 0, bMax = 100 " << map( 430, 0 , 1000, 0, 100 ) << endl;
//    qDebug() << "test map: val = 1, aMin = 0, aMax = 2, bMin = 0, bMax = 100 " << map( 1, 0 , 2, 0, 100 ) << endl;
}

//For some reason, deleting genes leads to an error
// deleting pointers that have not been allocated
GenomeSegment::~GenomeSegment()
{
//    qDebug() << "in genome segment deleting " << endl;
    qDeleteAll( genes );
}

void GenomeSegment::addGene( QString geneId, int start, int stop, bool ps)
{
    //qDebug() << "       adding gene to genome segment " << geneId << endl;
    genes << new Gene( geneId, start, stop, ps, this);
    geneHash.insertMulti( geneId, genes[ genes.size()-1] );//associate Gene with gene name

    if( stop > ntInGenomeSegment )//last gene in contig should be where the contig 'stops'
    {
        ntInGenomeSegment = stop; //later might be sth else- like an indep value- but this works for now
        maxNT = ntInGenomeSegment;
    }

    connect( genes[genes.size()-1], SIGNAL(alignmentEvent(QString, QPointF, int)), this, SIGNAL( throwAlignmentEvent(QString,QPointF,int)));

    layoutGenes(); //this is called each time a gene is added
                    //but then, when last gene is added, layout will be correct
}


void GenomeSegment::addGene( QString geneId, int start, int stop, bool ps,  QString geneLineOfText, QString fileType )
{
    //qDebug() << "       adding gene to genome segment " << geneId << endl;
    genes << new Gene( geneId, start, stop, ps, this);
    parseGeneLineOfText( genes.size()-1, geneLineOfText, fileType );
    geneHash.insertMulti( geneId, genes[ genes.size()-1] );//associate Gene with gene name

    if( stop > ntInGenomeSegment )//last gene in contig should be where the contig 'stops'
    {
        ntInGenomeSegment = stop; //later might be sth else- like an indep value- but this works for now
        maxNT = ntInGenomeSegment;
    }

    connect( genes[genes.size()-1], SIGNAL(alignmentEvent(QString, QPointF, int)), this, SIGNAL( throwAlignmentEvent(QString,QPointF,int)));

    layoutGenes(); //this is called each time a gene is added
                    //but then, when last gene is added, layout will be correct
}

void GenomeSegment::parseGeneLineOfText(int idx, QString geneLineOfText, QString fileType)
{
    if( fileType.contains( "reformatted") )
    {
        //Bt	CFB007412i	contig00125	Bt_CFB007412i_contig00125_G1.1	29	853	-	Cluster125	B1HSJ2	Putative uncharacterized protein 	no name	100	100	90	1e-136
        QStringList list = geneLineOfText.split("\t");

        genes[idx]->addAttribute( list[3], "GeneID");
//        genes[idx]->addAttribute( list[7], "ClusterId");
        genes[idx]->addAttribute( list[8], "UniprotId");
        genes[idx]->addAttribute( list[9], "Description");
        genes[idx]->addAttribute( list[10], "Internal Name");

    }


}

void GenomeSegment::layoutGenes()
{
    QRectF brect = boundingRect();
    float w = brect.width();
    float h = brect.height();
    for(int i = 0; i < genes.size(); i++)
    {
        int start = genes[i]->getStart();
        int stop = genes[i]->getStop();
        genes[i]->setWidth( (float)(stop-start)*(float)w/(float)ntInGenomeSegment );
        genes[i]->setHeight( h/2.0 );

        if( minNT > maxNT )//swap start and stop
        {
            int temp = start;
            start = stop;
            stop = temp;
        }
        genes[i]->setPos( map(start, minNT, maxNT, brect.x(), brect.x()+width  ) , brect.y() + h/4.0 );
        genes[i]->update();
    }
}

//map values from one range to another
float GenomeSegment::map( float valA, float aMin, float aMax, float bMin, float bMax )
{
    float valB = bMin + ( valA - aMin ) * ( bMax - bMin ) / ( aMax - aMin );
    return valB;
}


int GenomeSegment::getNTInGenomeSegment()
{
    return ntInGenomeSegment;
}

void GenomeSegment::setHeight( float newHeight )
{
    height = newHeight;
    dynHeight->setValue( height );

    // debate:  do I set it up so that genes are auto-resized or not ... ?
    //this would do it ... but I can't decide if that is the right thing to do
    //NOTE: was off- need to make sure it is ok for it to be on
    for( int i = 0; i < genes.size(); i++)
    {
        genes[i]->setHeight( height/2.0 );
        genes[i]->setPos( genes[i]->pos().x(), boundingRect().y() + height/4.0 );
    }

}

void GenomeSegment::setWidth( float newWidth )
{
    width = newWidth;
    rotationPt.setX( boundingRect().x() + (boundingRect().width()) / 2.0 );
    dynWidth->setValue( width );
}

void GenomeSegment::setSelectedGene(QString geneId)
{
    selectedGenes << geneId;
}

void GenomeSegment::setSelectedCluster(QString clusterId)
{
    selectedClusters << clusterId;
}

QStringList GenomeSegment::getSelectedGenes()
{
    return selectedGenes;
}

QStringList GenomeSegment::getSelectedClusters()
{
    return selectedClusters;
}

bool GenomeSegment::containsCluster(QString clusterId)
{
    for(int i = 0; i < genes.size(); i++)
    {
        if( genes[i]->getGeneId().compare( clusterId ) == 0 )
            return true;
    }
    return false;
}

//-----------------------------CONNECTION FUNCTIONS--------------------
void GenomeSegment::forgeConnection(GenomeSegment *other)
{

    //first connect all the genes in these genomes together
    if( this->genes.size() < other->genes.size() )
    {
        for(int i = 0 ; i < genes.size(); i++)
        {
            QString geneId = genes[i]->getGeneId(); //get all the names
            other->addConnection( geneId, genes[i] );//make connection between genes
        }
    }
    else
    {
        for(int i = 0 ; i < other->genes.size(); i++)
        {
            QString geneId = other->genes[i]->getGeneId(); //get all the names
            addConnection( geneId, other->genes[i] );//make connection between genes
        }
    }

    //then create connection between the genome segments -- in this case an alignment connection
    connect( this, SIGNAL( throwAlignmentEvent(QString,QPointF,int)), other, SLOT( catchAlignmentEvent(QString,QPointF,int)) );
    connect( other, SIGNAL( throwAlignmentEvent(QString,QPointF,int)), this, SLOT( catchAlignmentEvent(QString,QPointF,int)) );
}

void GenomeSegment::addConnection(QString geneId, Gene *gene)
{
    if( !geneHash.contains( geneId ) ) // do nothing if this gene is not in contig
        return;

    QList< QPointer< Gene > > matchingGenes = geneHash.values( geneId );//get list of matching genes

    for(int i = 0; i < matchingGenes.size(); i++ )
    {
        if( matchingGenes[i] != gene )
        {
            connect( gene, SIGNAL( hoverEnterOverGeneSignal()), matchingGenes[i], SLOT(hoverEnterOverGeneSlot()) );
            connect( gene, SIGNAL( hoverLeaveOverGeneSignal()), matchingGenes[i], SLOT(hoverLeaveOverGeneSlot()) );
            connect( matchingGenes[i], SIGNAL( hoverEnterOverGeneSignal()), gene, SLOT(hoverEnterOverGeneSlot()) );
            connect( matchingGenes[i], SIGNAL( hoverLeaveOverGeneSignal()), gene, SLOT(hoverLeaveOverGeneSlot()) );

            connect( gene, SIGNAL( geneSelectedSignal()), matchingGenes[i], SLOT( geneSelectedSlot()) );
            connect( matchingGenes[i], SIGNAL( geneSelectedSignal()), gene, SLOT( geneSelectedSlot()) );

            connect( gene, SIGNAL( changeGeneColorSignal(QColor)), matchingGenes[i], SLOT( changeGeneColorSlot(QColor)) );
            connect( matchingGenes[i], SIGNAL( changeGeneColorSignal(QColor)), gene, SLOT( changeGeneColorSlot(QColor)) );

            if( matchingGenes[i]->parentItem() != gene->parentItem() )//don't try to align genes in the same contig- if two of one gene in contig
            {
//                connect( gene, SIGNAL( alignWithThisGene(float, bool) ), matchingGenes[i], SLOT(calculateReAlignment(float, bool)) );
//                connect( matchingGenes[i], SIGNAL( alignWithThisGene(float, bool) ), gene, SLOT(calculateReAlignment(float, bool)) );
            }

        }
    }
}

//--------------------------- ALIGNMENT FUNCTIONS ------------------
void GenomeSegment::catchAlignmentEvent(QString geneToAlignAgainst, QPointF posInScene, int direction)
{
    qDebug() << "CAUGHT ALIGNMENT " << endl;

    if( !geneHash.contains(geneToAlignAgainst) )
        return;

    bool flip = false;
    if( minNT < maxNT ) //has not been flipped yet, need to know if should flip
    {
        if( geneHash.value( geneToAlignAgainst )->getIsPosStrand() && direction == 3 )//flip
        {
            flip  = true;
        }

        if( !geneHash.value( geneToAlignAgainst )->getIsPosStrand() && direction == 1 )//flip
        {
            flip  = true;
        }
    }
    else //already has been flipped , need to know if you need to flip back
    {
        if( geneHash.value( geneToAlignAgainst )->getIsPosStrand() && direction == 1 )//flip
        {
            flip  = true;
        }

        if( !geneHash.value( geneToAlignAgainst )->getIsPosStrand() && direction == 3 )//flip
        {
            flip  = true;
        }
    }

    if( flip )
    {
        int temp = minNT;
        minNT = maxNT;
        maxNT = temp;
        layoutGenes();
    }

    QPointF targetPos = mapToParent( mapFromScene( posInScene ) ); //posInScene must be mapped to item coordinates, then to parent contig coordinates
    QPointF geneToAlignPos = mapToParent( geneHash.value( geneToAlignAgainst )->pos() );//get gene's pos in genome segment, and map to contig

    //targeting on X
    float diff = targetPos.x() - geneToAlignPos.x();

//    target( pos().x() + diff , pos().y() );//dynamic movement
    setValues( pos().x() + diff , pos().y() );//quick movement
}



void GenomeSegment::setDistToMove(QPoint t)
{
    updated = true;
    distToMove = t;
}

void GenomeSegment::setFlip( bool f, QPointF rp )
{
    flip = f;
    rotationPt = mapToParent(rp);
}


//---------------------------INTERACTION FUNCTIONS------------------
void GenomeSegment::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    hovering = true;
    update();
}

void GenomeSegment::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    hovering = false;
    update();
}

//-----------------------------DRAW FUNCTIONS-----------------------
QRectF GenomeSegment::boundingRect() const
{
    return this->leftXCenterYRect();//QRectF( 0, -height/2.0, width, height);
}

QPainterPath GenomeSegment::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());
    return path;
}


void GenomeSegment::paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *w)
{
    //draw background box - size of genome segment
    backgroundColor.setRgb(60.0, 60.0, 60.0);
    if( hovering )
        backgroundColor.setRgb(90.0, 90.0, 90.0);
    painter->setBrush(backgroundColor);
    painter->setPen( Qt::NoPen );
    QRectF bRect = boundingRect();
    bRect.adjust(0, 1, 0, -1);
    painter->drawRect(bRect);

}
